﻿//============================================================================
// opto_module.cpp
// ?Copyright 2020, Opto GmbH, All rights reserved
// source file for Opto module C++ API
//============================================================================

// Opto C++ API
#include "opto_module.hpp"

// Opto C API
#include "OptoFramework.h"
#include "OptoModule.h"

// STL
#include<algorithm>
#include<string>
#include<iostream>
#include<map>

namespace Opto
{
	//============================================================================
	// Opto::ModuleInfos (08/02/2022)
	// Opto Module class, use to manage imaging modules
	//============================================================================
	ModuleInfos::ModuleInfos()
	{
	}

	ModuleInfos::~ModuleInfos()
	{
	}
	
	// copy constructor
	ModuleInfos::ModuleInfos(const ModuleInfos& mi)
	{
		name = mi.name;
		model = mi.model;
		serial = mi.serial;
		product = mi.product;
		configFile = mi.configFile;
		type = mi.type;
	}

	// assignment operator
	ModuleInfos& ModuleInfos::operator=(const ModuleInfos& mi)
	{
		name = mi.name;
		model = mi.model;
		serial = mi.serial;
		product = mi.product;
		configFile = mi.configFile;
		type = mi.type;
		return *this;
	}

	//============================================================================
	// Opto::Module (10/11/2020)
	// Opto Module class, use to manage imaging modules
	//============================================================================

	Module::Module() : m_module(nullptr)
	{
	}

	Module::~Module()
	{
	}

	// copy constructor
	Module::Module(const Module& m)
	{
		m_module = m.m_module;
	}

	// assignment operator
	Module& Module::operator=(const Module& m)
	{
		m_module = m.m_module;
		return *this;
	}


	// auto-discover modules, and create associated configuration files in "C:\Program Data\Opto\Fortress\Data"
	// module driver must has been previously loaded using OptoDevicesManager class
	void Module::autoDiscoverModules()
	{
		int error = OptoModule_DiscoverModules();
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::autoDiscoverModules()");
	}

	// enumerate modules
	std::vector<ModuleInfos> Module::enumerateModules(ModuleEnumerateMethod method)
	{
		unsigned long count;
		std::vector<ModuleInfos> vecModulesInfos;
		int error = OptoModule_EnumerateModules((OptoModuleEnumerateMethod)method, nullptr, &count);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::discoveredModules");		
		if(count > 0)
		{
			OptoModuleInfos* lpModInfos = new OptoModuleInfos[count];
			OptoModule_EnumerateModules((OptoModuleEnumerateMethod)method, &lpModInfos, &count);
			if(lpModInfos)
			{
				for (unsigned long i = 0; i < count; i++)
				{
					ModuleInfos modInfos;
					modInfos.name = std::string(lpModInfos[i].moduleName);
					modInfos.model = std::string(lpModInfos[i].model);
					modInfos.serial = std::string(lpModInfos[i].serial);
					modInfos.product = std::string(lpModInfos[i].product);
					modInfos.configFile = std::string(lpModInfos[i].configFilePath);
					modInfos.type = std::string(lpModInfos[i].moduleType);
					vecModulesInfos.push_back(modInfos);
				}
				delete [] lpModInfos;
			}
		}
		return vecModulesInfos;
	}


	// module management
	std::string Module::name()
	{
		char modName[OPTO_API_MAX_STRING_LENGTH];
		int error = OptoModule_GetModuleName(m_module.get(), modName, OPTO_API_MAX_STRING_LENGTH-1);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::name()");
		return std::string(modName);
	}

	// open an existing module and loads it's default configuration
	void Module::open(const std::string& name)
	{
		OptoModule* lpModule = nullptr;
		int error = OptoModule_OpenModule(name.c_str(), &lpModule);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::open()");
		// set a custom deleter to auto dispose the shared pointer when reference count reach 0
		m_module = std::shared_ptr<OptoModule>(lpModule, [](OptoModule* ptr) { OptoModule_CloseModule(ptr); });

	}

	// close this module instance 
	void Module::close()
	{
		// decrement instances reference count
		// module will be effectively close when m_module reference count reach 0
		// OptoModule_CloseModule() will then be called automatically 
		m_module.reset();
	}

	// create a new empty module from scratch
	void Module::create(const std::string& name)
	{
		OptoModule* lpModule = nullptr;
		int error = OptoModule_CreateModule(name.c_str(), &lpModule);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::create()");
		// set a custom deleter to auto dispose the shared pointer when reference count reach 0
		m_module = std::shared_ptr<OptoModule>(lpModule, [](OptoModule* ptr) { OptoModule_CloseModule(ptr); });
	}

	// returns a list of names of all devices used inside the module
	std::vector<std::string> Module::camerasNames()
	{
		std::vector<std::string> cams;
		unsigned long count = 0;
		int error = OptoModule_EnumerateModuleDevices(m_module.get(), OptoDeviceTypeCamera, nullptr, &count);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::camerasNames()");	
		OptoModuleDeviceInfos* lpInfos = new OptoModuleDeviceInfos[count];
		if(lpInfos)
		{
			error = OptoModule_EnumerateModuleDevices(m_module.get(), OptoDeviceTypeCamera, &lpInfos, &count);
			if(OPTO_FAILED(error))
			{
				delete [] lpInfos;
				throw Exception(error, "Opto::Module::devices()");
			}
			for(unsigned int i=0; i<count; i++)
				cams.push_back(std::string(lpInfos[i].deviceName));
			delete [] lpInfos;
		}
		return cams;
	}

	// return a weak pointer to the module owned by this object
	// this weak pointer can be locked by another module in order to temporarily share the ownership
	std::weak_ptr<OptoModule> Module::weakPointer() const
	{
		return std::weak_ptr<OptoModule>(m_module);
	}


	// configuration
	void Module::loadConfiguration(const std::string& filepath)
	{
		int error = OptoModule_LoadConfiguration(m_module.get(), filepath.c_str());
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::loadConfiguration()");
	}

	void Module::saveConfiguration(const std::string& filepath)
	{
		int error = OptoModule_SaveConfiguration(m_module.get(), filepath.c_str());
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::saveConfiguration()");
	}


	void Module::loadDefaultConfiguration()
	{
		int error = OptoModule_LoadDefaultConfiguration(m_module.get());
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::loadDefaultConfiguration()");
	}


	void Module::saveDefaultConfiguration()
	{
		int error = OptoModule_SaveDefaultConfiguration(m_module.get());
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::saveDefaultConfiguration()");
	}

	void Module::resetDefaultConfiguration()
	{
		int error = OptoModule_ResetDefaultConfiguration(m_module.get());
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::resetDefaultConfiguration()");
	}

	// image acquisition
	void Module::setupCameraAcquisition(const std::string& camName, bool continuous, unsigned long long buffers)
	{
		int error = OptoModule_SetupCameraAcquisition(m_module.get(), camName.c_str(), continuous, buffers);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::setupCameraAcquisition()");
	}

	void Module::startCameraAcquisition(const std::string& camName)
	{
		int error = OptoModule_StartCameraAcquisition(m_module.get(), camName.c_str());
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::startCameraAcquisition()");
	}

	void Module::stopCameraAcquisition(const std::string& camName)
	{
		int error = OptoModule_StopCameraAcquisition(m_module.get(), camName.c_str());
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::stopCameraAcquisition()");
	}

	void Module::cleanupCameraAcquisition(const std::string& camName)
	{
		int error = OptoModule_CleanupCameraAcquisition(m_module.get(), camName.c_str());
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::cleanupCameraAcquisition()");
	}

	void Module::grabCameraImage(const std::string& camName, Image& image, BufferMode mode, unsigned long long desiredBuffer, unsigned long long* obtainedBuffer, int timeoutMs)
	{
		int error = OptoModule_GrabCameraImage(m_module.get(), camName.c_str(), image.weakPointer().lock().get(), (OptoCameraBufferMode)mode, desiredBuffer, obtainedBuffer, timeoutMs);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::grabCameraImage()");
	}

	void Module::grabCameraBuffer(const std::string& camName, void* bufferIn, int bufferWidth, int bufferHeight, int bufferBytesPerPixel, 
								 int bufferRowBytes, BufferMode mode, unsigned long long desiredBuffer, unsigned long long* obtainedBuffer, int timeoutMs)
	{
		int error = OptoModule_GrabCameraBuffer(m_module.get(), camName.c_str(), bufferIn, bufferWidth, bufferHeight, bufferBytesPerPixel, bufferRowBytes,
											   (OptoCameraBufferMode)mode, desiredBuffer, obtainedBuffer, timeoutMs);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::grabCameraBuffer()");
	}


	void Module::cameraImageInfos(const std::string& camName, Image::Type* type, int* width, int* height, int* bitsPerPixel, int* bytesPerPixel)
	{
		OptoImageType ltype;
		int error = OptoModule_GetCameraImageInfos(m_module.get(), camName.c_str(), &ltype, width, height, bitsPerPixel, bytesPerPixel);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::cameraImageInfos()");
		if(type)
			*type = (Image::Type)ltype;
	}

	void Module::cameraCalibrationInfos(const std::string& camName, double* pixelWidth, double* pixelHeight, CalibrationUnits* units)
	{
		OptoCalibrationUnits lunits;
		int error = OptoModule_GetCameraCalibration(m_module.get(), camName.c_str(), pixelWidth, pixelHeight, &lunits);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::cameraCalibrationInfos()");
		if (units)
			*units = (Module::CalibrationUnits)lunits;
	}


	// attributes
	std::vector<std::string> Module::attributes(const std::string& root, Attribute::Visibility visibility)
	{
		std::vector<std::string> attrs;
		unsigned long count = 0;
		int error = OptoModule_GetAttributesInfos(m_module.get(), root.c_str(), (OptoAttributeVisibility)visibility, nullptr, &count);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::attributes()");	 

		OptoModuleAttributeInfos* lpInfos = new OptoModuleAttributeInfos[count];
		if(lpInfos)
		{
			error = OptoModule_GetAttributesInfos(m_module.get(), root.c_str(), (OptoAttributeVisibility)visibility, &lpInfos, &count);
			if(OPTO_FAILED(error))
			{
				delete [] lpInfos;
				throw Exception(error, "Opto::Module::attributes()");
			}
			for(unsigned long i=0; i<count; i++)
				attrs.push_back(lpInfos[i].name);
			delete [] lpInfos;
		}
		return attrs;
	}

	bool Module::hasAttribute(const std::string& attrName)
	{
		OptoAttributeType type;
		int error = OptoModule_GetAttributeType(m_module.get(), attrName.c_str(), &type);
		if(OPTO_FAILED(error))
			return false;
		return true;
	}


	std::string Module::attributeDescription(const std::string& attrName)
	{
		char desc[OPTO_API_MAX_STRING_LENGTH];
		int error = OptoModule_GetAttributeDescription(m_module.get(), attrName.c_str(), desc, OPTO_API_MAX_STRING_LENGTH-1);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::attributeDescription()");
		return std::string(desc);
	}

	std::string Module::attributeTooltip(const std::string& attrName)
	{
		char tooltip[OPTO_API_MAX_STRING_LENGTH];
		int error = OptoModule_GetAttributeTooltip(m_module.get(), attrName.c_str(), tooltip, OPTO_API_MAX_STRING_LENGTH-1);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::attributeTooltip()");
		return std::string(tooltip);
	}

	std::string Module::attributeUnit(const std::string& attrName)
	{
		char units[OPTO_API_MAX_STRING_LENGTH];
		int error = OptoModule_GetAttributeUnits(m_module.get(), attrName.c_str(), units, OPTO_API_MAX_STRING_LENGTH-1);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::attributeUnit()");
		return std::string(units);
	}


	void Module::setAttributeValue(const std::string& attrName, const std::string& stringValue)
	{
		int error = OptoModule_SetAttributeValue(m_module.get(), attrName.c_str(), OptoValueTypeString, stringValue.c_str());
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::setAttributeValue(std::string)");
	}

	void Module::setAttributeValue(const std::string& attrName, bool boolValue)
	{
		int error = OptoModule_SetAttributeValue(m_module.get(), attrName.c_str(), OptoValueTypeBool, boolValue);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::setAttributeValue(bool)");
	}


	void Module::setAttributeValue(const std::string& attrName, int i32Value)
	{
		int error = OptoModule_SetAttributeValue(m_module.get(), attrName.c_str(), OptoValueTypeI32, i32Value);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::setAttributeValue(int)");
	}

	void Module::setAttributeValue(const std::string& attrName, long long i64Value)
	{
		int error = OptoModule_SetAttributeValue(m_module.get(), attrName.c_str(), OptoValueTypeI64, i64Value);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::setAttributeValue(long long)");
	}


	void Module::setAttributeValue(const std::string& attrName, double f64Value)
	{
		int error = OptoModule_SetAttributeValue(m_module.get(), attrName.c_str(), OptoValueTypeF64, f64Value);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::setAttributeValue(double)");
	}

	void Module::setAttributeOnePushCommand(const std::string& attrName)
	{
		int error = OptoModule_OnePushCommand(m_module.get(), attrName.c_str());
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::setAttributeOnePushCommand()");
	}

	std::string Module::attributeStringValue(const std::string& attrName, int decimals)
	{
		char value[OPTO_API_MAX_STRING_LENGTH];
		int error = OptoModule_GetAttributeStringValue(m_module.get(), attrName.c_str(), value, OPTO_API_MAX_STRING_LENGTH, decimals);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::attributeStringValue()");
		return std::string(value);
	}

	bool Module::attributeBoolValue(const std::string& attrName)
	{
		bool value;
		int error = OptoModule_GetAttributeValue(m_module.get(), attrName.c_str(), OptoValueTypeBool, &value);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::attributeBoolValue()");
		return value;
	}

	int Module::attributeI32Value(const std::string& attrName)
	{
		int value;
		int error = OptoModule_GetAttributeValue(m_module.get(), attrName.c_str(), OptoValueTypeI32, &value);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::attributeI32Value()");
		return value;
	}

	long long Module::attributeI64Value(const std::string& attrName)
	{
		long long value;
		int error = OptoModule_GetAttributeValue(m_module.get(), attrName.c_str(), OptoValueTypeI64, &value);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::attributeI64Value()");
		return value;
	}

	double Module::attributeF64Value(const std::string& attrName)
	{
		double value;
		int error = OptoModule_GetAttributeValue(m_module.get(), attrName.c_str(), OptoValueTypeF64, &value);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::attributeF64Value()");
		return value;
	}

	Opto::Attribute::Type Module::attributeType(const std::string& attrName)
	{
		OptoAttributeType type;
		int error = OptoModule_GetAttributeType(m_module.get(), attrName.c_str(), &type);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::attributeType()");
		return (Opto::Attribute::Type)type;
	}

	Opto::Attribute::Visibility Module::attributeVisibility(const std::string& attrName)
	{
		OptoAttributeVisibility visibility;
		int error = OptoModule_GetAttributeVisibility(m_module.get(), attrName.c_str(), &visibility);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::attributeVisibility()");
		return (Opto::Attribute::Visibility)visibility;
	}


	std::string Module::attributeDisplayName(const std::string& attrName)
	{
		char dispName[OPTO_API_MAX_STRING_LENGTH];
		int error = OptoModule_GetAttributeDisplayName(m_module.get(), attrName.c_str(), dispName, OPTO_API_MAX_STRING_LENGTH);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::attributeDisplayName()");
		return std::string(dispName);
	}


	bool Module::isAttributeReadOnly(const std::string& attrName)
	{
		bool read = false;
		bool write = false;
		int error = OptoModule_IsAttributeReadableWritable(m_module.get(), attrName.c_str(), &read, &write);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::isAttributeReadOnly()");
		return read && !write;
	}

	double Module::attributeMinimumRange(const std::string& attrName)
	{
		Opto::Attribute::Type type = Module::attributeType(attrName);
		switch(type)
		{
			default:
			{
				throw Exception(OptoErrorAttributeType, "Opto::Module::attributeMinimumRange()");
				break;
			}

			case Opto::Attribute::Type::I32:
			{
				int min;
				int error = OptoModule_GetAttributeNumericRange(m_module.get(), attrName.c_str(), OptoValueTypeI32, &min, nullptr, nullptr);
				if(OPTO_FAILED(error))
					throw Exception(error, "Opto::Module::attributeMinimumRange()");
				return (double)min;
				break;
			}

			case Opto::Attribute::Type::I64:
			{
				long long min;
				int error = OptoModule_GetAttributeNumericRange(m_module.get(), attrName.c_str(), OptoValueTypeI64, &min, nullptr, nullptr);
				if(OPTO_FAILED(error))
					throw Exception(error, "Opto::Module::attributeMinimumRange()");
				return (double)min;
				break;
			}

			case Opto::Attribute::Type::F64:
			{
				double min;
				int error = OptoModule_GetAttributeNumericRange(m_module.get(), attrName.c_str(), OptoValueTypeF64, &min, nullptr, nullptr);
				if(OPTO_FAILED(error))
					throw Exception(error, "Opto::Module::attributeMinimumRange()");
				return min;
				break;
			}
		}
	}

	double Module::attributeMaximumRange(const std::string& attrName)
	{
		Opto::Attribute::Type type = Module::attributeType(attrName);
		switch(type)
		{
			default:
			{
				throw Exception(OptoErrorAttributeType, "Opto::Module::attributeMaximumRange()");
				break;
			}

			case Opto::Attribute::Type::I32:
			{
				int max;
				int error = OptoModule_GetAttributeNumericRange(m_module.get(), attrName.c_str(), OptoValueTypeI32, nullptr, &max, nullptr);
				if(OPTO_FAILED(error))
					throw Exception(error, "Opto::Module::attributeMaximumRange()");
				return (double)max;
				break;
			}

			case Opto::Attribute::Type::I64:
			{
				long long max;
				int error = OptoModule_GetAttributeNumericRange(m_module.get(), attrName.c_str(), OptoValueTypeI64, nullptr, &max, nullptr);
				if(OPTO_FAILED(error))
					throw Exception(error, "Opto::Module::attributeMaximumRange()");
				return (double)max;
				break;
			}

			case Opto::Attribute::Type::F64:
			{
				double max;
				int error = OptoModule_GetAttributeNumericRange(m_module.get(), attrName.c_str(), OptoValueTypeF64, nullptr, &max, nullptr);
				if(OPTO_FAILED(error))
					throw Exception(error, "Opto::Module::attributeMaximumRange()");
				return max;
				break;
			}
		}
	}


	double Module::attributeStep(const std::string& attrName)
	{
		Opto::Attribute::Type type = Module::attributeType(attrName);
		switch(type)
		{
			default:
			{
				throw Exception(OptoErrorAttributeType, "Opto::Module::attributeStep()");
				break;
			}

			case Opto::Attribute::Type::I32:
			{
				int step;
				int error = OptoModule_GetAttributeNumericRange(m_module.get(), attrName.c_str(), OptoValueTypeI32, nullptr, nullptr, &step);
				if(OPTO_FAILED(error))
					throw Exception(error, "Opto::Module::attributeStep()");
				return (double)step;
				break;
			}

			case Opto::Attribute::Type::I64:
			{
				long long step;
				int error = OptoModule_GetAttributeNumericRange(m_module.get(), attrName.c_str(), OptoValueTypeI64, nullptr, nullptr, &step);
				if(OPTO_FAILED(error))
					throw Exception(error, "Opto::Module::attributeStep()");
				return (double)step;
				break;
			}

			case Opto::Attribute::Type::F64:
			{
				double step;
				int error = OptoModule_GetAttributeNumericRange(m_module.get(), attrName.c_str(), OptoValueTypeF64, nullptr, nullptr, &step);
				if(OPTO_FAILED(error))
					throw Exception(error, "Opto::Module::attributeStep()");
				return step;
				break;
			}
		}
	}

	std::vector<std::string> Module::attributeEnumItemNames(const std::string& attrName)
	{
		OptoModuleAttributeExtendedInfos* lpAttrInfos = nullptr;
		int error = OptoModule_GetAttributeExtentedInfos(m_module.get(), attrName.c_str(), &lpAttrInfos);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::attributeEnumItemNames()");
		std::vector<std::string> itemsNames;
		if(lpAttrInfos)
		{
			for(int i=0; i<lpAttrInfos->nbEnumItems; i++)
				itemsNames.push_back(lpAttrInfos->enumNames[i]);
			OptoModule_DisposeAttributesExtentedInfos(lpAttrInfos, 1);
		}
		return itemsNames;
	}

	std::vector<int> Module::attributeEnumItemValues(const std::string& attrName)
	{
		OptoModuleAttributeExtendedInfos* lpAttrInfos = nullptr;
		int error = OptoModule_GetAttributeExtentedInfos(m_module.get(), attrName.c_str(), &lpAttrInfos);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::attributeEnumItemValues()");
		std::vector<int> itemsIds;
		if(lpAttrInfos)
		{
			for(int i=0; i<lpAttrInfos->nbEnumItems; i++)
				itemsIds.push_back(lpAttrInfos->enumIDs[i]);
			OptoModule_DisposeAttributesExtentedInfos(lpAttrInfos, 1);
		}
		return itemsIds;
	}

	std::vector<bool> Module::attributeEnumItemStates(const std::string& attrName)
	{
		OptoModuleAttributeExtendedInfos* lpAttrInfos = nullptr;
		int error = OptoModule_GetAttributeExtentedInfos(m_module.get(), attrName.c_str(), &lpAttrInfos);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::attributeEnumItemStates()");
		std::vector<bool> itemsStates;
		if(lpAttrInfos)
		{
			for(int i=0; i<lpAttrInfos->nbEnumItems; i++)
				itemsStates.push_back(lpAttrInfos->enumStates[i]);
			OptoModule_DisposeAttributesExtentedInfos(lpAttrInfos, 1);
		}
		return itemsStates;
	}

	void Module::attributeOnePush(const std::string& attrName)
	{
		int error = OptoModule_SetAttributeValue(m_module.get(), attrName.c_str(), OptoValueTypeBool, true);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::attributeOnePush()");
	}



	void Module::setFavoritesAttributes(const std::vector<std::string>& favorites)
	{
		std::vector<char*> cstrings;
		cstrings.reserve(favorites.size());
		for(size_t i=0; i<favorites.size(); ++i)
			cstrings.push_back(const_cast<char*>(favorites[i].c_str()));
		int error = OptoModule_SetFavoritesAttributes(m_module.get(), cstrings.data(), (unsigned int)cstrings.size());
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::setFavoritesAttributes()");
	}


	std::vector<std::string> Module::favoritesAttributes()
	{
		std::vector<std::string> favorites = Module::attributes("", Attribute::Visibility::Favorites);
		return favorites;
	}

	bool Module::isAttributeFavorite(const std::string& attrName)
	{
		bool isFavorite = false;
		int error = OptoModule_IsAttributeFavorite(m_module.get(), attrName.c_str(), &isFavorite);
		if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::isAttributeFavorite()");
		return isFavorite;
	}

	void Module::registerFrameCallback(const std::string& camName, OptoFrameCallbackPtr cbFunc, void* cbData)
	{
		 int error = OptoModule_RegisterFrameCallback(m_module.get(), camName.c_str(), cbFunc, cbData);
		 if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::registerFrameCallback()");
	}


	void Module::unregisterFrameCallback(const std::string& camName)
	{
		 int error = OptoModule_RegisterFrameCallback(m_module.get(), camName.c_str(), nullptr, nullptr);
		 if(OPTO_FAILED(error))
			throw Exception(error, "Opto::Module::unregisterFrameCallback()");
	}



}; // Opto::Module namespace
